iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI Engineering

30天拆Agent:從Repo看設計系列 第 6

Day 6|HolmesGPT:當 Agent 自己掌握 Tool Loop,控制點會放在哪裡?

  • 分享至 

  • xImage
  •  

前兩天看 anthropics/oncall-kit,比較像是在 Claude Code 這個既有 Runtime 上,加入 Skill、SOP 與 Human Gate。

今天換一個完全不同的例子:HolmesGPT

HolmesGPT 是一個開源的 SRE Agent,目前也是 CNCF Sandbox Project。它主要拿來調查 production incident,例如 Kubernetes Pod 異常、Prometheus 指標、Log、Database、Cloud resource 等問題。

例如可以直接問:

payment-api 為什麼一直 CrashLoopBackOff?

HolmesGPT 不只是把問題丟給 LLM。

它會讓模型自己決定要不要查:

Kubernetes
Prometheus
Grafana
Datadog
Database
Bash
Internet
MCP
...

查到結果後,再把結果送回模型,繼續下一步 investigation。

官方 README 把這件事直接稱為:

agentic loop

這也是今天我想看的主題:

HolmesGPT 不只定義 Agent 要做什麼,它連 Model → Tool → Result → Model 這條執行迴圈都自己掌握。這會帶來什麼差別?


先用一張圖看懂 HolmesGPT

如果先不碰細節,HolmesGPT 可以簡化成:

User
 │
 ▼
Prompt / Skills
 │
 ▼
ToolCallingLLM
 │
 ▼
LLM
 │
 ├── 不需要 Tool ──────────→ Answer
 │
 └── 需要 Tool
         │
         ▼
     Tool Executor
         │
         ▼
       Tool
         │
         ▼
    Tool Result
         │
         └──────────────→ LLM

這裡最重要的元件是:

ToolCallingLLM

它在:

holmes/core/tool_calling_llm.py

HolmesGPT 並不是用 LangGraph 畫一張固定 Workflow:

查 Pod
→ 查 Log
→ 查 Prometheus
→ 回答

而是每一輪讓模型看目前資訊,再決定下一步。

例如:

User:
payment-api 為什麼 CrashLoopBackOff?

第一輪模型可能決定:

先查 Pod status

Tool Result 回來後:

OOMKilled
restartCount = 12

模型看到結果,再決定:

查 Pod memory limit

接著可能再查:

Prometheus memory usage

最後資料夠了,才停止 Tool Calling 並回答。

所以它真正的流程是:

LLM
 ↓
決定下一個 Tool
 ↓
Runtime 執行
 ↓
Observation
 ↓
LLM
 ↓
再決定下一步

這其實很像 ReAct,但差別在 Action 怎麼被執行

如果熟悉 ReAct,HolmesGPT 的核心流程其實很接近:

Reason
→ Action
→ Observation
→ 再決定下一步

HolmesGPT 同樣是讓模型根據目前資訊決定下一個 Tool,拿到 Tool Result 後,再繼續下一輪判斷。

差別在於,經典 ReAct 比較著重「模型如何交錯 Reasoning、Action、Observation」;HolmesGPT 則把中間的 Action 做成更完整的工程流程:

LLM
↓
Structured Tool Call
↓
Tool.invoke()
↓
Approval / Validation
↓
_invoke()
↓
StructuredToolResult
↓
LLM

也就是說,HolmesGPT 並不是另一種完全不同於 ReAct 的 Agent 思路。

更準確地說,它是:

ReAct-like 的決策循環,加上一套 structured tool calling 與可控制的 Tool Execution Pipeline。

後面真正值得看的,也正是 ReAct 圖裡常被簡化成一個箭頭的:

Action → Observation

HolmesGPT 到底在這個箭頭中間,放了哪些控制機制。


「模型決定下一步」不代表「模型控制執行」

HolmesGPT 的確讓 LLM 決定:

下一步要呼叫哪個 Tool?
參數要填什麼?
拿到結果後還要不要繼續?

但模型並沒有直接執行 Tool。

真正的關係比較像:

LLM
= 提出 Action

Runtime
= 決定怎麼執行這個 Action

例如模型回傳:

bash(
  command="docker ps"
)

它只是產生一個 Tool Call。

真正執行:

docker ps

的是 HolmesGPT 的程式。

這個差異看起來很小,卻是整個 Repo 最值得看的地方。

因為既然 HolmesGPT 自己握有執行權,它就可以在:

Model Decision

和:

Real Side Effect

中間插入控制。


Tool 並不是模型想用什麼就有什麼

在 Model Call 前,HolmesGPT 先準備本輪可以使用的 Tools。

它把不同 Data Source 包成 Toolset,例如:

Kubernetes Toolset
Prometheus Toolset
Grafana Toolset
Bash Toolset
Database Toolset
MCP Toolset

最後由 Runtime 決定哪些 Tool 真正提供給模型。

也就是:

Runtime
 ↓
準備 Available Tools
 ↓
送給 LLM
 ↓
LLM 從這批能力裡選

第一層控制因此已經出現:

Code 先決定 Model 擁有哪些 executable capabilities。

Skill 則是另外一回事。

HolmesGPT 也有 Skill,它比較像 SOP:

遇到 Pod CrashLoopBackOff:

1. 先看 Pod status
2. 看 current / previous logs
3. 看 events
4. 再比對 rollout

Skill 告訴模型:

怎麼調查

Tool 則回答:

你真的能做什麼

這兩件事要分開。


真正重要的是:Tool Call 出現之後發生什麼?

接下來才進到今天真正想看的 Code。

所有 Tool 都繼承 HolmesGPT 的 Tool

核心位置:

holmes/core/tools.py

其中真正重要的是:

Tool.invoke()

簡化實際程式後,大概長這樣:

def invoke(params, context):

    if not context.user_approved:

        approval = self._get_approval_requirement(
            params,
            context
        )

        if approval and approval.needs_approval:

            return StructuredToolResult(
                status=APPROVAL_REQUIRED
            )

    params = self._coerce_params(params)

    result = self._invoke(
        params=params,
        context=context
    )

    return result

這十幾行,其實已經說明 HolmesGPT 的控制邊界。

模型產生:

Tool Call

之後,不是直接進:

_invoke()

而是:

Tool Call
   ↓
Tool.invoke()
   ↓
Approval Check
   ↓
Parameter Processing
   ↓
_invoke()

真正的 Tool implementation 在:

_invoke()

因此:

只要程式停在 Tool.invoke(),真正的 Side Effect 就還沒有發生。


用 Bash Tool 看會更清楚

HolmesGPT 的 Bash Tool 是一個很好的實際例子。

相關程式在:

holmes/plugins/toolsets/bash/bash_toolset.py

Bash Tool 在真正執行 command 以前,會先跑:

requires_approval()

裡面會把 command 拿去做 validation。

結果大致分三種:

ALLOWED
DENIED
APPROVAL_REQUIRED

概念上:

LLM 想執行 Bash command
          │
          ▼
    validate_command()
          │
     ┌────┼────────────┐
     │    │            │
 ALLOWED DENIED  APPROVAL_REQUIRED
     │    │            │
     ▼    ▼            ▼
 執行   拒絕        等 User Approval

實際 requires_approval() 裡可以看到:

if validation_result.status == ValidationStatus.APPROVAL_REQUIRED:

    return ApprovalRequirement(
        needs_approval=True,
        reason="Command requires approval...",
    )

接著回到共用的:

Tool.invoke()

發現:

approval.needs_approval == True

就直接回:

APPROVAL_REQUIRED

此時 _invoke() 還沒有執行。


而且 Bash Tool 裡又檢查了一次

這裡有一個很有意思的細節。

真正執行 Bash command 的 _invoke() 裡,還是會重新 Validate。

如果理論上需要 Approval 的 command 居然跑進 _invoke(),程式不是默默執行,而是:

if validation_result.status == ValidationStatus.APPROVAL_REQUIRED:

    return StructuredToolResult(
        status=ERROR,
        error="Command requires approval but was not approved. This may be a bug."
    )

Code 裡甚至直接留了一句 Comment:

This indicates requires_approval() was bypassed.

意思很清楚:

第一道:
Tool.invoke() 應該先擋

第二道:
就算前面的 approval 流程被繞過,
_invoke() 仍不要直接執行

這就不是:

Prompt 裡拜託 Model 不要做

而是:

Code 明確定義:
這個狀態不能執行

假設只靠 Skill,會發生什麼?

假設 SOP 寫:

執行高風險操作以前,
一定要先取得使用者確認。

正常情況:

Skill
 ↓
LLM 理解
 ↓
先問 User
 ↓
User Confirm
 ↓
Tool

但 LLM 可能犯錯。

它可能直接產生 Tool Call。

如果架構是:

LLM
 ↓
Tool Call
 ↓
直接執行

那 SOP 就只是 Instruction。

HolmesGPT 則多了一層:

Skill
 ↓
LLM
 ↓
Tool Call
 ↓
Tool.invoke()
 ↓
Approval Check
 ↓
真正執行

所以 Skill 和 Runtime Control 是兩件不同的事。

可以很簡單地分:

Skill
= 告訴 Model 正確行為

Runtime
= 限制實際可發生的行為

為什麼這比「用了哪個 Agent Framework」更值得看?

看 Agent Repo 時,很容易先問:

用了 LangGraph 嗎?
用了 ReAct 嗎?
用了 Skill 嗎?
支援 MCP 嗎?

但 HolmesGPT 讓我開始多問一個問題:

從模型產生 Tool Call,到外部世界真的被修改,中間經過誰?

例如:

LLM
 ↓
Tool Schema
 ↓
Tool.invoke()
 ↓
Approval
 ↓
Validation
 ↓
Credential
 ↓
External System

如果把所有控制都寫進 Prompt:

請不要做危險的事。
請務必先確認。
請遵守 SOP。

等於把安全性建立在:

Model 永遠判斷正確

這個假設上。

HolmesGPT 的做法則接受另一個現實:

Model 可以犯錯。

但錯誤的 Tool Call
不應該自動等於
錯誤的 Side Effect。

結論

第一次看 HolmesGPT,可以先不用記它所有 Toolset、Memory、Skill 或 Evaluation。

先記住這張圖就好:

User
 ↓
LLM
 ↓
Tool Call
 ↓
Tool.invoke()
 ↓
Approval / Validation
 ↓
_invoke()
 ↓
External System
 ↓
Tool Result
 ↓
LLM

HolmesGPT 的 Model 仍然有很大的自主性。

它決定:

下一步查什麼
使用哪個 Tool
Tool Parameter 怎麼填
什麼時候停止 investigation

但真正執行 Tool 的權力仍留在 Runtime。

所以今天看完這個 Repo,我留下的不是:

HolmesGPT 有 Runtime Approval。

而是更一般化的一條 Agent 設計原則:

Model 可以決定 Action,但不要讓 Model 同時擁有 Action 的最終執行權。

前面用 oncall-kit 看到了 SOP 與 Human Gate;再用 Claude Code 看 Host Runtime 如何把 Gate 做成強制阻擋。

HolmesGPT 則讓這件事更清楚:

如果 Runtime 就掌握在自己手上,Gate 最自然的位置,就是 Model Decision 與 Real Execution 之間。

References


上一篇
Day 5|Claude Code 的強制阻擋設定
系列文
30天拆Agent:從Repo看設計6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言